package cn.spdb.harrier.common.utils;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;


/**
 * 0-000000000-0000000000-0000000000-0000000000-0-0000000000-000000000000 long
 * 64 1为符号位 41位毫秒时间 10位机器编码 12位毫秒并发
 * 
 * @author T-luzl
 *
 */
public class SnowFlakeBuidID {

	// 时间标记
	private final static long twepoch = 1588230837059L;
	// 机器表示编码位数
	private final static long wokerIdBits = 10L;
	// 毫秒内自增位数
	private final static long sequenceBits = 12L;
	// 最大机器标示ID
	private final static long wokerIdMax = -1L ^ (-1L << wokerIdBits);
	// 最大自增id
	private final static long sequenceIdMax = -1L ^ (-1L << sequenceBits);
	// 机器标记迁移
	private final static long woerkeIdShift = sequenceBits;
	// 时间戳迁移
	private final static long timestampLeftShift = wokerIdBits + sequenceBits;

	// 时间戳
	private long lastTimestamp = 0L;

	private final long workeid;

	private long sequence = 0;

	public SnowFlakeBuidID(int workeid) {
		if (wokerIdMax <= workeid) {
			throw new RuntimeException(String.format("wokerIdMax is %d ,workeid %d is over", wokerIdMax, workeid));
		}
		this.workeid = workeid;
	}

/**
 * 无参构造函数，自动根据本机信息生成唯一的工作节点ID（workeid）
 */
public SnowFlakeBuidID() {
    long id = 0;
    StringBuffer buffer = new StringBuffer();

    try {
        // 获取本地IP地址
        InetAddress ip = InetAddress.getLocalHost();

        // 获取网络接口对象
        NetworkInterface networkInterface = NetworkInterface.getByInetAddress(ip);

        if (networkInterface != null) {
            // 获取MAC地址并追加到buffer中
            byte[] mac = networkInterface.getHardwareAddress();
            buffer.append(mac);
        } else {
            // 如果没有获取到网络接口信息，则使用默认字符串"loacl"
            buffer.append("loacl");
        }

        // 获取JVM运行时名称（通常包含进程ID）
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            // 将JVM名称追加到buffer中
            buffer.append(name);
        }

        // 使用hashCode生成一个随机数，并限制在wokerIdMax范围内作为工作节点ID
        id = (buffer.toString().hashCode() & 0xFFFFF) % (wokerIdMax + 1);
    } catch (Exception e) {
        // 异常处理，打印异常堆栈信息
        e.printStackTrace();
    }

    // 设置最终生成的worker ID
    this.workeid = id;
}


	private long timesTamp() {
		return System.currentTimeMillis();
	}

/**
 * 生成下一个全局唯一ID
 *
 * @return 返回一个long类型的唯一ID
 */
public synchronized long getNextId() {
    // 获取当前时间戳（毫秒）
    long timesTamp = timesTamp();

    // 如果当前时间小于上一次生成ID的时间，说明时钟回拨，抛出异常
    if (timesTamp < lastTimestamp) {
        throw new RuntimeException(String.format(
                "clock moved backwards,refusing to generate id for %d milliseconds", lastTimestamp - timesTamp));
    }

    // 如果当前时间与上次相同，进入同一毫秒内处理逻辑
    if (timesTamp == lastTimestamp) {
        // 序列号自增，通过位运算确保不超过最大并发数
        sequence = (sequence + 1) & sequenceIdMax;

        // 如果序列号达到最大值，则等待下一毫秒
        if (sequence == 0) {
            timesTamp = timesTampNext(timesTamp);
        }
    } else {
        // 如果进入新的毫秒，重置序列号为0
        sequence = 0L;
    }

    // 更新最后生成ID的时间戳
    lastTimestamp = timesTamp;

    // 拼接最终的ID：(时间戳部分 << 时间位移) | (工作节点ID << 序列位移) | 序列号
    long nextId = ((timesTamp - twepoch) << timestampLeftShift) | (workeid << woerkeIdShift) | sequence;
    return nextId;
}


/**
 * 等待直到时间戳超过指定的当前时间戳，以避免生成重复ID
 *
 * @param timesTamp 当前时间戳（毫秒）
 * @return 返回一个新的、大于输入时间戳的时间戳
 */
private long timesTampNext(final long timesTamp) {
    // 获取当前系统时间戳
    long tmptimesTamp = this.timesTamp();

    // 循环等待，直到时间戳前进到下一个毫秒
    while (tmptimesTamp > timesTamp) {
        tmptimesTamp = this.timesTamp();
    }

    // 返回新的时间戳
    return tmptimesTamp;
}

}
